#include "Song.h"

namespace LibMusic{

float getCoeffCorr(int _X, Mp3& _Mp3)
{
	if(_Mp3.GetFrequence(_X) > 400)
	{
		return 0.4f ;
	}
	else
	{
		return 1.2f ;
	}
}

struct RythmComp
{
	bool operator() (const Rythm& _Lhs, const Rythm& _Rhs) const
	{
		return _Lhs.GetBf() < _Rhs.GetBf() ;
	}
} ;

struct RythmCompTime
{
	bool operator() (const Rythm& _Lhs, const Rythm& _Rhs) const
	{
		return _Lhs.GetBegin() < _Rhs.GetBegin() ;
	}
} ;


// Constructor()
Song::Song(const std::string & _Mp3Path) : m_Mp3(_Mp3Path)
{
	if(!ifstream(GetSongName(_Mp3Path).c_str()))
	{
		m_Mp3.DoDFT(40) ;

		int nb = 0 ;
		float moy = 0 ;
		int max = 0 ;
		for(int y = 0 ; y < m_Mp3.m_HeightFFT ; y++)
		{
			for(int x = 0 ; x < m_Mp3.m_WidthFFT ; x++)
			{
				// cf veut dire "current frequence"
				double cf = MaxZoneValue(x,y,1,0) ;
				if(cf > m_Mp3.m_MaxAmp*0.30*getCoeffCorr(x, m_Mp3))
				{
					m_Rythm.push_back(Rythm(40 * y, 40 * y, x, x)) ;
					max = (max>x)?max:x ;
					moy = static_cast<float>(moy*nb + x)/static_cast<float>(nb+1) ;
					nb++ ;
				}
			}
		}

		std::cout << "frequence max : " << max << "   = " << m_Mp3.GetFrequence(max) << std::endl ;
		std::cout << "frequence moy : " << moy << "   = " << m_Mp3.GetFrequence((int) moy) << std::endl ;
		
		for(std::vector<Rythm>::iterator it = m_Rythm.begin() ; it != m_Rythm.end() ; ++it)
		{
			if( m_Mp3.GetFrequence((int) it->GetBf()) > 3500)
			{
				it = m_Rythm.erase(it) ;
				if(it == m_Rythm.end())
					break ;
			}
		}
		std::cout << m_Rythm.size() << std::endl ;
		double maxHf = 0, minHf = 0 ;
		int nbRythm(0) ;
		do
		{
			nbRythm = m_Rythm.size() ;
			for(std::vector<Rythm>::iterator current = m_Rythm.begin() ; current != m_Rythm.end() ; ++current )
			{
				for(std::vector<Rythm>::iterator next = (current+1) ; next != m_Rythm.end() ; ++next)
				{
					// 2 cas : on est sur la meme ligne ou pas
					if(next->GetBegin() == current->GetBegin())
					{
						if(	   (std::abs(next->GetBf() - current->GetBf()) < 5)
							|| (std::abs(next->GetHf() - current->GetHf()) < 5)
							|| (std::abs(next->GetBf() - current->GetHf()) < 5)
							|| (std::abs(next->GetHf() - current->GetBf()) < 5) )
						{
							current->Fusion((*next)) ;
							next = m_Rythm.erase(next) ;
							maxHf = (maxHf<current->GetHf())?current->GetHf():maxHf ;
							minHf = (minHf<current->GetHf())?current->GetHf():minHf ;
							if(next == m_Rythm.end())
								break ;
						}
					}
					else if(next->GetBegin() - current->GetEnd() < 121)
					{
						if(	  ((next->GetBf() >= current->GetBf() && next->GetBf() <= current->GetHf())
							|| (next->GetHf() <= current->GetHf() && next->GetHf() >= current->GetBf()))
							&& (static_cast<float>(next->GetHf()-next->GetBf())/static_cast<float>(current->GetHf()-current->GetBf()) > 0.5) )
						{
							current->Fusion((*next)) ;
							next = m_Rythm.erase(next) ;
							maxHf = (maxHf<current->GetHf())?current->GetHf():maxHf ;
							minHf = (minHf<current->GetHf())?current->GetHf():minHf ;
							if(next == m_Rythm.end())
								break ;
						}
					} else
					{
						break ;
					}
				}
			}
			std::cout << "Current : " << m_Rythm.size() << "\t Last : " << nbRythm << std::endl ;
		}
		while(nbRythm - m_Rythm.size() != 0) ;

		// Retire les rythmes trop petits :
		for(std::vector<Rythm>::iterator current = m_Rythm.begin() ; current != m_Rythm.end() ;  )
		{
			if(std::abs(current->GetEnd() - current->GetBegin()) < 121)
			{
				current = m_Rythm.erase(current) ;
				if(current == m_Rythm.end())
					break ;
			} else if(std::abs(current->GetHf() - current->GetBf()) < 4)
			{
				current = m_Rythm.erase(current) ;
				if(current == m_Rythm.end())
					break ;
			} else
			{
				current++ ;
			}
		}

		// trie du vector d'obstacles :
		std::sort(m_Rythm.begin(), m_Rythm.end(), RythmComp()) ;
		int part0, part1, part2, part3 ;
		part0 = m_Rythm.size()/4 ;
		part1 = m_Rythm.size()/2 ;
		part2 = (m_Rythm.size()/4) * 3 ;
		part3 = m_Rythm.size() ;

		int i = 0 ;
		for(std::vector<Rythm>::iterator current = m_Rythm.begin() ; current != m_Rythm.end() ; ++current)
		{
			if(i < part0)
			{
				current->SetType(Rythm::Bot) ;
			} else if(i < part1)
			{
				current->SetType(Rythm::Left) ;
			} else if(i < part2)
			{
				current->SetType(Rythm::Right) ;
			} else
			{
				current->SetType(Rythm::Top) ;
			}
			i++ ;
		}
		std::sort(m_Rythm.begin(), m_Rythm.end(), RythmCompTime()) ;


		std::cout << "Before : " << m_Rythm.size() << std::endl ;
		for(std::vector<Rythm>::iterator current = m_Rythm.begin() ; current != m_Rythm.end() ; ++current)
		{
			for(std::vector<Rythm>::iterator next = (current+1) ; next != m_Rythm.end() ; ++next)
				{
					if(current->GetType() == next->GetType())
					{
						if(current->GetEnd() > next->GetBegin()-700)
						{
							next = m_Rythm.erase(next) ;
							maxHf = (maxHf<current->GetHf())?current->GetHf():maxHf ;
							minHf = (minHf<current->GetHf())?current->GetHf():minHf ;
							if(next == m_Rythm.end())
								break ;
						}
					}
				}
		}
		std::cout << "After : " << m_Rythm.size() << std::endl ;

		std::cout << m_Rythm.size() << std::endl ;
		std::cout << "Ecriture de " << Song::GetSongName(_Mp3Path)  << std::endl;
		WriteLvlPng(Song::GetSongName(_Mp3Path) + ".png") ;
		WriteLvl(Song::GetSongName(_Mp3Path)) ;
		std::cout << "OK" << std::endl;
	}
	else
	{
		std::cout << "Le fichier : \"" << GetSongName(_Mp3Path) << "\" existe deja !" << std::endl ;
	}
}	

// Destructor
Song::~Song()
{
}

std::string Song::GetSongName(std::string _Mp3Path){
	return _Mp3Path + ".song" ;
}


// WriteLvl
void Song::WriteLvl(string _Name)
{
	ofstream fichier(_Name.c_str(), ios::out | ios::trunc) ;

	if(!fichier)
	{
		cout << "Erreur ecriture fichier : " << _Name << endl ;
	}
	else
	{
		fichier << this << endl ;
		fichier.close() ;
	}
}

// WriteLvlPng
void Song::WriteLvlPng(string _Name)
{
	int height = m_Mp3.m_HeightFFT ;
	int width = m_Mp3.m_WidthFFT ;
	vector<double> image(height*width, 0) ;

	for(std::vector<Rythm>::iterator it = m_Rythm.begin() ; it != m_Rythm.end() ; ++it)
	{
		for(int i = (int) it->GetBf() ; i < it->GetHf() ; i++)
		{
			image[static_cast<int>(it->GetBegin()/40)*width + i] = 255 ;
		}
		for(int i = (it->GetBegin()/40)+1 ; i < (it->GetEnd()/40) ; i++)
		{
			for(int j = (int) it->GetBf() ; j < it->GetHf() ; j++)
			{
				image[i*width + j] = 130 ;
			}
		}
	}

	Mp3::WritePNG(static_cast<double*>(&image[0]), height, width, "laaaaaaaa.png") ;
}

void Song::PlayMusic()
{
	m_Mp3.Play() ;
}

// MaxZoneValue
double Song::MaxZoneValue(int _X, int _Y, int _Dx, int _Dy)
{
	double ret = 0;
	for(int i = _X-_Dx ; i <= _X+_Dx ; i++)
	{
		for(int j = _Y-_Dy ; j <= _Y+_Dy ; j++)
		{
			if(i < 0 || i >= m_Mp3.m_WidthFFT || j < 0 || j >= m_Mp3.m_HeightFFT)
				continue ;
			else
			{
				ret = (ret > (*m_Mp3.m_CurrentFFT)[i+j*m_Mp3.m_WidthFFT])?ret:(*m_Mp3.m_CurrentFFT)[i+j*m_Mp3.m_WidthFFT] ;
			}
		}
	}
	return ret ;
}


// =================================================================
// operator<<
std::ostream& operator<< (std::ostream& out, const Song* lvl)
{
	out << "name Test Song 2" << endl ;
	out << "duration 20000" << endl ;
	out << "obstacles" << endl ;
	for(std::vector<Rythm>::const_iterator it = lvl->m_Rythm.begin() ; it != lvl->m_Rythm.end() ; it++)
	{
		out << &(*it) << std::endl ;
	}

	return out ;
}

}